package jmind.core.image.code;

import jmind.base.lang.Pair;
import jmind.base.util.RandUtil;
import jmind.core.image.ImageUtil;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;

/**
 * https://jjjssh.iteye.com/blog/2422844
 * 根据源图片，剪切出两张图
 * @author weibo
 * @date 2019/07/16
 */
public class ImageValidateCode {

   private static Color getRandColor(int fc,int bc){//给定范围获得随机颜色
        if(fc>255) fc=255;
        if(bc>255) bc=255;
        int r=fc+RandUtil.nextInt(bc-fc);
        int g=fc+RandUtil.nextInt(bc-fc);
        int b=fc+RandUtil.nextInt(bc-fc);
        return new Color(r,g,b);

    }

    /**
     * 早年生成验证码
     * @return
     */
    public static Pair<String,BufferedImage> getImageCode(){
        // 在内存中创建图象
        int width=82, height=20;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 获取图形上下文
        Graphics g = image.getGraphics();
        // 设定背景色
        g.setColor(getRandColor(200,250));
        g.fillRect(0, 0, width, height);
        //设定字体
        g.setFont(new Font("Dialog",Font.BOLD,16));
        //画边框
        //g.setColor(new Color());
        //g.drawRect(0,0,width-1,height-1);
        // 随机产生155条干扰线，使图象中的认证码不易被其它程序探测到
        g.setColor(getRandColor(160,200));
        for (int i=0;i<155;i++)
        {

            int x = RandUtil.nextInt(width);
            int y = RandUtil.nextInt(height);
            int xl = RandUtil.nextInt(12);
            int yl = RandUtil.nextInt(12);
            g.drawLine(x,y,x+xl,y+yl);

        }

        // 取随机产生的认证码(6个字符)
        String sRand="";
        String rand="";
        for (int i=0;i<6;i++) {
            if (RandUtil.randomBoolean()) {
                int j = RandUtil.nextInt(26);
                int k = RandUtil.randomBoolean()? (j + 65) : (j + 97);
                rand = String.valueOf((char)k);
            } else {
                rand = String.valueOf(RandUtil.nextInt(10));

            }
            sRand+=rand.toUpperCase();
            // 将认证码显示到图象中
            g.setColor(new Color(20 + RandUtil.nextInt(110), 20 + RandUtil.nextInt(110),
                20 + RandUtil.nextInt(110))); //调用函数出来的颜色相同，可能是因为种子太接近，所以只能直接生成
            g.drawString(rand, 13 * i + 6, 16 + RandUtil.nextInt(3));
        }
            g.dispose();

         return Pair.of(sRand,image);

        }

    /**
     *
     * @param width  宽
     * @param height  高
     * @param codeCount  字母个数
     * @param interferingLine  干扰线
     * @return
     * @throws IOException
     */
    public static Pair<String,BufferedImage> getVerifyImg(int width,int height,int codeCount,int interferingLine) throws IOException {

        int fontHeight = height/2;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics graphic = image.getGraphics();
        graphic.setColor(Color.getColor("F8F8F8"));
        graphic.fillRect(0, 0, width, height);

        Color[] colors = new Color[] { Color.BLUE, Color.GRAY, Color.GREEN, Color.RED, Color.BLACK, Color.ORANGE,
            Color.CYAN };
        // 在 "画板"上生成干扰线条 ( 50 是线条个数)
        for (int i = 0; i < interferingLine; i++) {
            graphic.setColor(colors[RandUtil.nextInt(colors.length)]);
            final int x = RandUtil.nextInt(width);
            final int y = RandUtil.nextInt(height);
            final int w = RandUtil.nextInt(20);
            final int h = RandUtil.nextInt(20);
            final int signA = RandUtil.randomBoolean() ? 1 : -1;
            final int signB = RandUtil.randomBoolean() ? 1 : -1;
            graphic.drawLine(x, y, x + w * signA, y + h * signB);
        }
        StringBuilder  sb=new StringBuilder();
        // 在 "画板"上绘制字母
        graphic.setFont(new Font("Comic Sans MS", Font.BOLD, fontHeight));
        for (int i = 0; i < codeCount; i++) {
            final int temp = RandUtil.nextInt(26) + 97;
            String s = String.valueOf((char) temp);
            sb.append(s);
            graphic.setColor(colors[RandUtil.nextInt(colors.length)]);
            graphic.drawString(s, i * (width / 6), height - (height / 3));
        }
        graphic.dispose();

        //ByteArrayOutputStream os = new ByteArrayOutputStream();
        //ImageIO.write(image, "png", os);
        //byte b[] = os.toByteArray();
        return Pair.of(sb.toString(),image);
    }

    /**
     * 根据原图生成一张裁剪图
     * @param x  裁剪位置x轴
     * @param y  裁剪位置y轴
     * @param width  裁剪图片宽
     * @param height  裁剪图片高
     * @param sourceFile  原图
     * @param targetFile   生成的新的裁剪图
     * @throws IOException
     */
    public static File cut(int x,int y,int width,int height,String sourceFile,String targetFile) throws IOException {//裁剪方法
        FileInputStream is=null;
        ImageInputStream iis=null;
        try{
            //读取原始图片
            is=new FileInputStream(sourceFile);
            //ImageReader声称能够解码指定格式
            Iterator<ImageReader> it= ImageIO.getImageReadersByFormatName("jpg");
            ImageReader reader=it.next();
            //获取图片流
            iis=ImageIO.createImageInputStream(is);
            //将iis标记为true（只向前搜索）意味着包含在输入源中的图像将只按顺序读取
            reader.setInput(iis, true);
            //指定如何在输入时从 Java Image I/O框架的上下文中的流转换一幅图像或一组图像
            ImageReadParam param=reader.getDefaultReadParam();
            //定义空间中的一个区域
            Rectangle rect=new Rectangle(x, y, width, height);
            //提供一个 BufferedImage，将其用作解码像素数据的目标。
            param.setSourceRegion(rect);
            //读取索引imageIndex指定的对象
            BufferedImage bi=reader.read(0, param);
            //保存新图片
            File file = new File(targetFile);
            ImageIO.write(bi, "png", new File(targetFile));
            return file;
        }finally{
            if(is!= null) {
                is.close();
            }
            if(iis != null) {
                iis.close();
            }
        }
    }

    /**
     * 随机生成一张移动的小图和 遮罩的图
     * @param width 裁剪图片宽
     * @param height 裁剪图片高
     * @param sourceFile  原图
     * @return
     * @throws IOException
     */
    public static ImageCode cutByTemplate(String sourceFile, int width, int height) throws IOException {
        //原图
        BufferedImage oriImage=ImageIO.read(new File(sourceFile));
        //移动图,小图
        BufferedImage newSrc=new BufferedImage(oriImage.getWidth(), oriImage.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);
        //对比图 ，带遮罩的原图
        BufferedImage newSrc2=new BufferedImage(oriImage.getWidth(), oriImage.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);
        int border=20 ;
        //10,width-200
        int x=RandUtil.nextInt(oriImage.getWidth()-width-border)+border;

        int y=RandUtil.nextInt(oriImage.getHeight()-height-border)+border;
        //固定圆半径为5
        int c_r=20;
        //r平方
        double rr=Math.pow(c_r, 2);
        //圆心的位置

        //x+c_r+10;//圆心x坐标必须在(x+r,x+with-r)范围内
        int c_a= RandUtil.nextInt(width-2*c_r)+(x+c_r);
        int c_b=y;

        //第二个圆（排除圆内的点）

        int c2_a=x;
        //y+c_r+50;//圆心y坐标必须在(y+r,y+height-r)范围内
        int c2_b=RandUtil.nextInt(height-2*c_r)+(y+c_r);

        for(int i=0;i<oriImage.getWidth();i++){
            for(int j=0;j<oriImage.getHeight();j++){
                //(x-a)²+(y-b)²=r²中，有三个参数a、b、r，即圆心坐标为(a，b)，半径r。
                double f=Math.pow((i-c_a), 2)+Math.pow((j-c_b), 2);

                double f2=Math.pow((i-c2_a), 2)+Math.pow((j-c2_b), 2);

                int rgb=oriImage.getRGB(i,j);
                //在矩形内
                if(i>=x&&i<(x+width) &&j>=y&&j<(y+height) && f2>=rr){
                    //块范围内的值
                    in(newSrc, newSrc2, i, j, rgb);
                }else if(f<=rr){
                    //在圆内
                    in(newSrc, newSrc2, i, j, rgb);
                }else{
                    //剩余位置设置成透明
                    out(newSrc, newSrc2, i, j, rgb);
                }

            }
        }
        ImageCode imageCode=new ImageCode();
        imageCode.setX(x);
        imageCode.setY(y);
        imageCode.setTemplateImage(newSrc);
        imageCode.setTargetImage(newSrc2);
        return imageCode;
    }

    private static void in(BufferedImage newSrc,BufferedImage newSrc2,int i,int j,int rgb){
        newSrc.setRGB(i, j, rgb);
        //原图设置变灰
        int r = (0xff & rgb);
        int g = (0xff & (rgb >> 8));
        int b = (0xff & (rgb >> 16));
        rgb = r + (g << 8) + (b << 16) + (100 << 24);
        //rgb = r + (g << 8) + (b << 16);
        newSrc2.setRGB(i, j, rgb);
    }

    private static  void out(BufferedImage newSrc,BufferedImage newSrc2,int i,int j,int rgb){
        newSrc.setRGB(i, j, 0x00ffffff);
        newSrc2.setRGB(i, j, rgb);
    }


    public static void main(String[]args)throws Exception {


        //int blockWidth=100;
        //int blockHeight=100;
        //ImageCode imageCode = cutByTemplate("/Users/weibo/Downloads/vcode/2.jpg", blockWidth,
        //    blockHeight );//图片大小是固定，位置是随机
        //
        ////生成移动图
        //ImageIO.write(imageCode.getTemplateImage(), "png", new File("/Users/weibo/Downloads/vcode/v3.png"));
        ////生成对比图
        //ImageIO.write(imageCode.getTargetImage(), "png", new File("/Users/weibo/Downloads/vcode/v4.png"));

        Pair<String, BufferedImage> verifyImg = getVerifyImg(600, 400, 6, 100);
        ImageUtil.write(verifyImg.getSecond(),"png","/Users/weibo/Downloads/vcode/a1.png");
        System.err.println(verifyImg.getFirst());

       verifyImg = getImageCode();

        ImageUtil.write(verifyImg.getSecond(),"png","/Users/weibo/Downloads/vcode/a2.png");
        System.err.println(verifyImg.getFirst());

    }
}
